﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.ComponentModel;
using MahApps.Metro;
using MahApps.Metro.Controls;
using System.IO;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Data;
using System.Windows.Media.Animation;
using BooruViewer.Annotations;
using BooruViewer.Models;
using ControlzEx;
using MahApps.Metro.IconPacks;
using BooruViewer.Properties;
using MahApps.Metro.Controls.Dialogs;

namespace BooruViewer
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : MetroWindow, INotifyPropertyChanged
    {
        /******************\
        |*    BINDINGS    *|
        \******************/
        
        // Collection of images
        public ObservableCollection<BImage> ImagesCollection { get; set; } = new ObservableCollection<BImage>();
        // Thumbnail size
        public double ThumbWidth { get; set; } = Settings.ThumbWidth;
        public double ThumbHeight { get; set; } = Settings.ThumbHeight;
        // Columns count
        public int ColumnsCount { get; set; } = 5;
        // Ratings
        public bool SearchSafe { get; set; }
        public bool SearchQuestionable { get; set; }
        public bool SearchExplicit { get; set; }
        // Score
        public int? LowerScore { get; set; }
        public int? HigherScore { get; set; }
        // Page number
        public int PageNumber { get; set; } = 1;
        // Selected image
        public BImage CurrentImage { get; set; }
        public int CurrentImageIndex { get; set; }
        // Tags string
        public string Tags { get; set; }
        

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        /***********************\
        |*    OTHER GLOBALS    *|
        \***********************/

        // Settings
        private static readonly Settings Settings = Settings.Default;

        // Animation handlers
        private bool _filterExpanded;


        /*****************\
        |*    METHODS    *|
        \*****************/

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;

            #region LOAD SETTINGS ON STARTUP

            // Set window state
            WindowState = Settings.WindowState;

            // Get app accent and theme from settings and set it
            ThemeManager.ChangeAppStyle(Application.Current,
                                        ThemeManager.GetAccent(Settings.Accent),
                                        ThemeManager.GetAppTheme(Settings.Theme));

            // Set accent combobox data source
            Accent_ComboBox.ItemsSource = Enum.GetValues(typeof(LooksManager.Accents)).Cast<LooksManager.Accents>();
            // Set accent selection
            Accent_ComboBox.SelectedItem = Enum.Parse(typeof(LooksManager.Accents), Settings.Accent);

            // Set theme button icon based on settings
            if (Settings.Theme == "BaseLight")
            {
                DarkMode_Icon.Kind = PackIconFontAwesomeKind.SunRegular;
                _darkModeIconToggle = false;
            }
            else
            {
                DarkMode_Icon.Kind = PackIconFontAwesomeKind.MoonRegular;
                _darkModeIconToggle = true;
            }

            #endregion
        }

        #region REUSABLES

        private async void LoadImages()
        {
            LoaderGrid.Visibility = Visibility.Visible;

            if (Tags != SearchBox.Text)
            {
                Tags = SearchBox.Text;
                ImagesCollection = new ObservableCollection<BImage>();
                PageNumber = 1;
                OnPropertyChanged(nameof(ImagesCollection));
                OnPropertyChanged(nameof(PageNumber));
                MainScrollViewer.ScrollToTop();
            }


            // Break if query is empty
            if (String.IsNullOrEmpty(Tags)) return;

            // Build array of Ratings
            List<Rating> ratings = new List<Rating>();
            if (SearchSafe) ratings.Add(Rating.Safe);
            if (SearchQuestionable) ratings.Add(Rating.Questionable);
            if (SearchExplicit) ratings.Add(Rating.Explicit);

            // Create Query
            Query query = new Query(
                Tags,
                ratings.ToArray(),
                PageNumber,
                LowerScore,
                HigherScore,
                Settings.DerpiToken
            );

            // Provider
            string provider = ProviderCBox.SelectedItem.ToString().Split(':')[1].Trim(' ').ToUpper();

            // Task responsible for grabbing images
            Task<ObservableCollection<BImage>> imgTask = Task.Factory.StartNew(() =>
            {
                string call;
                string json;

                List<BImage> imgList = null;
                List<BImage> imgCurrList = ImagesCollection.ToList();

                switch (provider)
                {
                    case "DERPIBOORU":
                        call = query.BuildDerpibooru();
                        json = JsonGetter.GetJson(call);
                        Derpi derpi = Derpi.FromJson(json);
                        imgList = derpi.DerpiToImages();
                        break;

                    case "DANBOORU":
                        call = query.BuildDanbooru();
                        json = JsonGetter.GetJson(call);
                        Danbooru[] danbo = Danbooru.FromJson(json);
                        imgList = danbo.DanbooruToImages();
                        break;

                    case "ATFBOORU":
                        call = query.BuildDanbooru("https://atfbooru.ninja");
                        json = JsonGetter.GetJson(call);
                        Danbooru[] atf = Danbooru.FromJson(json);
                        imgList = atf.DanbooruToImages();
                        break;

                    case "E621":
                        call = query.BuildE621();
                        json = JsonGetter.GetJson(call);
                        E621[] e621 = E621.FromJson(json);
                        imgList = e621.E621ToImages();
                        break;

                    case "GELBOORU":
                        call = query.BuildGel();
                        json = JsonGetter.GetJson(call);
                        Gelbooru[] gel = Gelbooru.FromJson(json);
                        imgList = gel.GelToImages();
                        break;
                }

                if (imgList == null) return new ObservableCollection<BImage>(imgCurrList);

                foreach (BImage img in imgList)
                {
                    imgCurrList.Add(img);
                }

                return new ObservableCollection<BImage>(imgCurrList);
            });

            // Task that, heh, unloads the loader
            Task<bool> isImgTaskDone = imgTask.ContinueWith((antecedent) => antecedent.IsCompleted);

            LoaderGrid.Visibility = (await isImgTaskDone) ? Visibility.Collapsed : Visibility.Visible;

            ImagesCollection = await imgTask;
            OnPropertyChanged(nameof(ImagesCollection));
            
            PageNum.Value = PageNumber;
            PageNumber++;
        }

        #endregion

        #region ANIMATIONS

        bool AnimateGrid(Grid grid, bool handler, int height = 100, double time = 0.5)
        {
            if (handler)
            {
                var anim = new DoubleAnimation(0, (Duration)TimeSpan.FromSeconds(time));
                grid.BeginAnimation(ContentControl.HeightProperty, anim);
                return false;
            }
            else
            {
                var anim = new DoubleAnimation(height, (Duration)TimeSpan.FromSeconds(time));
                grid.BeginAnimation(ContentControl.HeightProperty, anim);
                return true;
            };
        }

        #endregion

        #region SETTINGS AND MENUS

        // Handle main menu open/close
        bool _menuToggle;
        private void MenuBtn_Click(object sender, RoutedEventArgs e)
        {
            MenuFlyout.IsOpen = !_menuToggle;
            _menuToggle = !_menuToggle;
            
            // Save settings
            Settings.Save();
        }

        // Handle accent selection combobox
        private void Accent_ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            Tuple<AppTheme, Accent> theme = ThemeManager.DetectAppStyle(Application.Current);

            ThemeManager.ChangeAppStyle(Application.Current,
                                        ThemeManager.GetAccent(Accent_ComboBox.SelectedItem.ToString()),
                                        theme.Item1);

            // Save in _settings
            Settings.Accent = Accent_ComboBox.SelectedItem.ToString();
        }

        // Handle theme selection switch
        private bool _darkModeIconToggle;
        private void DarkMode_Toggle_Click(object sender, RoutedEventArgs e)
        {
            Tuple<AppTheme, Accent> completeTheme = ThemeManager.DetectAppStyle(Application.Current);
            string theme;

            if (_darkModeIconToggle)
            {
                theme = "BaseLight";

                _darkModeIconToggle = false;
                DarkMode_Icon.Kind = PackIconFontAwesomeKind.SunRegular;
            }
            else
            {
                theme = "BaseDark";

                _darkModeIconToggle = true;
                DarkMode_Icon.Kind = PackIconFontAwesomeKind.MoonRegular;
            }

            // Apply chosen theme
            ThemeManager.ChangeAppStyle(Application.Current,
                                            completeTheme.Item2,
                                            ThemeManager.GetAppTheme(theme));

            // Save in _settings
            Settings.Theme = theme;
        }

        // Handle titlebar minification switch
        private void MinifyTitlebar_Toggle_Click(object sender, RoutedEventArgs e)
        {
            if (ShowTitleBar)
            {
                ShowTitleBar = false;
                icoMinifyTitlebar.Kind = PackIconMaterialKind.EyeOff;
            }
            else
            {
                ShowTitleBar = true;
                icoMinifyTitlebar.Kind = PackIconMaterialKind.Eye;
            }

            // Save in _settings
            Settings.MinifiedTitle = ShowTitleBar;
        }

        // Handle KoFi
        private void BtnKoFi_Click(object sender, RoutedEventArgs e)
        {
            System.Diagnostics.Process.Start("https://ko-fi.com/H2H365N9");
        }

        // Handle closing
        private void MetroWindow_Closing(object sender, CancelEventArgs e)
        {
            // Save _settings
            Settings.Save();
        }


        // Fullscreen
        private bool _isFullscreen;
        private bool _isCustomFullscreen;
        private void ToggleFullscreen_Click(object sender, RoutedEventArgs e)
        {
            _isCustomFullscreen = true;

            WindowState = _isFullscreen ? Settings.WindowState : WindowState.Maximized;
            ResizeMode = _isFullscreen ? ResizeMode.CanResize : ResizeMode.NoResize;

            _isFullscreen = !_isFullscreen;
            IgnoreTaskbarOnMaximize = _isFullscreen;

            _isCustomFullscreen = false;
        }

        #endregion

        #region HANDLE EVENTS

        // Handle window state changed
        private void MainWindow_OnStateChanged(object sender, EventArgs e)
        {
            if (!_isCustomFullscreen)
            {
                Settings.WindowState = WindowState;
                Settings.Save();
            }
        }

        // Handle window size changes
        private void MainWindow_OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            ColumnsCount = Helpers.CalculateColumns(ImagesView.ActualWidth, ThumbWidth);
            OnPropertyChanged(nameof(ColumnsCount));
        }

        // Handle ScrolViewer scroll
        private void ScrollViewer_OnScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            var scrollViewer = (ScrollViewer)sender;

            if (ImagesCollection.Count <= 0) return;
            if (Math.Abs(scrollViewer.VerticalOffset - scrollViewer.ScrollableHeight) < 0.05)
                LoadImages();
        }

        // Thumbnail size change
        private void ThumbSizeNum_OnValueChanged(object sender, RoutedPropertyChangedEventArgs<double?> e)
        {
            if (ThumbHeightNum.Value != null) ThumbHeight = (double) ThumbHeightNum.Value;
            if (ThumbWidthNum.Value != null) ThumbWidth = (double) ThumbWidthNum.Value;
            OnPropertyChanged(nameof(ThumbHeight));
            OnPropertyChanged(nameof(ThumbWidth));
            Settings.ThumbHeight = ThumbHeight;
            Settings.ThumbWidth = ThumbWidth;
            ColumnsCount = Helpers.CalculateColumns(ImagesView.ActualWidth, ThumbWidth);
            OnPropertyChanged(nameof(ColumnsCount));
        }

        #endregion

        #region BUTTON HANDLERS

        // Handle filters open button
        private void FilterBtn_OnClick(object sender, RoutedEventArgs e)
        {
            _filterExpanded = AnimateGrid(FiltersGrid, _filterExpanded, 54, 0.3);
        }

        // Handle search button
        private void SearchBtn_OnClick(object sender, RoutedEventArgs e)
        {
            LoadImages();
        }

        // Handle image list clear button
        private void ClearBtn_OnClick(object sender, RoutedEventArgs e)
        {
            ImagesCollection = new ObservableCollection<BImage>();
            PageNumber = 1;
            OnPropertyChanged(nameof(ImagesCollection));
            OnPropertyChanged(nameof(PageNumber));
            MainScrollViewer.ScrollToTop();
        }

        // Handle Derpi token button
        private void DerpiTokenBtn_OnClick(object sender, RoutedEventArgs e)
        {
            DerpiTokenWindow.IsOpen = !DerpiTokenWindow.IsOpen;
        }

        // Handle Derpi token save button
        private void DerpiTokenSaveBtn_OnClick(object sender, RoutedEventArgs e)
        {
            Settings.DerpiToken = DerpiTokenBox.Password;
            Settings.Save();
        }

        // Handle image clicks
        private void ImagesView_OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (!(sender is Grid item)) return;
            if (!(item.DataContext is BImage img)) return;

            Title = img.Id;

            CurrentImage = img;
            OnPropertyChanged(nameof(CurrentImage));

            CurrentImageIndex = ImagesCollection.IndexOf(CurrentImage);
            OnPropertyChanged(nameof(CurrentImageIndex));

            ImageViewFlyout.IsOpen = true;
        }

        // Handle closing full image view
        private void BackBtn_OnClick(object sender, RoutedEventArgs e)
        {
            ImageViewFlyout.IsOpen = false;
        }

        #endregion

        #region KEYBOARD CONTROLS

        private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
        {
            if (!ImageViewFlyout.IsOpen) return;

            switch (e.Key)
            {
                case Key.A:
                case Key.Left:
                    CurrentImage = ImagesCollection[(CurrentImageIndex - 1).Clamp(0, ImagesCollection.Count - 1)];
                    OnPropertyChanged(nameof(CurrentImage));
                    CurrentImageIndex = (CurrentImageIndex - 1).Clamp(0, ImagesCollection.Count - 1);
                    OnPropertyChanged(nameof(CurrentImageIndex));
                    break;

                case Key.D:
                case Key.Right:
                    CurrentImage = ImagesCollection[(CurrentImageIndex + 1).Clamp(0, ImagesCollection.Count - 1)];
                    OnPropertyChanged(nameof(CurrentImage));
                    CurrentImageIndex = (CurrentImageIndex + 1).Clamp(0, ImagesCollection.Count - 1);
                    OnPropertyChanged(nameof(CurrentImageIndex));
                    if (Math.Abs(ImagesCollection.Count - CurrentImageIndex) < 5) LoadImages();
                    break;
            }
        }

        #endregion

    }
}
